home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Programming Sound Cards
/
Programming Sound Cards.iso
/
sound_56
/
blaster.pas
next >
Wrap
Pascal/Delphi Source File
|
1995-01-01
|
42KB
|
1,179 lines
{$A+,B-,D+,E-,F-,G+,I-,L+,N-,O-,P-,Q-,R-,S-,T-,V-,X+,Y+}
{$M 16384,0,655360}
Unit Blaster;
{ -------------------------------------------------------------------- }
{ This is a specially for detecting and using the soundblaster card. }
{ autodetect routines work for dsp adresses 210 to 280, }
{ 8bit dmachannels 0,1,3/ 16bit dmachannel 5 and for interrupts 2,5,7 }
{ (known problems with IRQ2) }
{ -------------------------------------------------------------------- }
{ no more work on autodetection - wait for craigs source, takes better }
{ advantage to our knowledge about different SB models, we got in last }
{ time. i.e. you can read IRQ#/DMA8/DMA16 from mixer port on SB16 }
{ --------------------------------------------------------------------- }
{ I read about 16Bit improvment for SB PRO3/4.0 but don't know about - }
{ anybody can help me with that ? }
{ how to use it ... any specifications for it ? }
{ --------------------------------------------------------------------- }
{ STATUS: TESTED ON SB16/ASP,SB PRO2,SB2.0 }
{ COMMENTs READY }
{ KNOWN PROBLEMS : - IRQ2 detection is wrong on some maschines }
{ - no 16bit IRQ autodetection :( }
{ - no check if values are right in }
{ * UseBlasterEnv }
{ * InputBlasterValues }
{ - no mixedup detection (i.e. some values as }
{ parameters and detect the rest) }
{ I though about this while I had a SB PRO and a }
{ SB16 at the same time 'inside' on base 220h,240h }
{ --------------------------------------------------------------------- }
Interface
{ the different SoundBlaster versions and its hardware : }
{ 0. - no soundblaster present
1. Soundblaster 1.0/1.5
23kHz(mono) 8bit no mixer
2. Soundblaster Pro
45kHz(mono) 23kHz(stereo) 8bit mixer
3. Soundblaster 2.0/ Audioblaster 2.5
45kHz(mono) 8bit audiob. with mixer
4. Soundblaster Pro3/Audioblaster Pro 4.0
45kHz(mono) 23kHz(stereo) 8bit mixer
5. Soundblaster Pro (Mircochannel)
Special version for PS/2 - technical data = 4
6. Soundblaster 16/16ASP
45kHz(mono) 45kHz(stereo) 8/16bit mixer
DSP versions :
SoundBlaster 1.0/1.5 1.xx
SoundBlaster 2.0/2.5 2.xx
SoundBlaster Pro/PRO3/PRO4 3.xx
SoundBlaster 16/ASP 4.xx
}
CONST defaultvolume=31; { full power =;) }
IRQ_TABLE:array[0..15] of byte = ($08,$09,$0a,$0B,$0C,$0D,$0E,$0F,
$70,$71,$72,$73,$74,$75,$76,$77);
VAR
stereo_possible:Boolean; (* flag if stereo is possible on detected SB *)
_16Bit_possible:Boolean; (* flag if 16bit play is possible on detected SB *)
maxstereorate:word; (* max stereo samplerate on detected SB *)
maxmonorate:word; (* max mono samplerate on detected SB *)
Stereo:Boolean; (* flag if stereo-play on/off *)
_16Bit:Boolean; (* flag if 16bit-play on/off *)
SBNo:byte; (* SoundBlaster typ (look some rows above) *)
signeddata:boolean; (* play signed data ? (only on SB16 possible) *)
IRQ_No:Byte; (* IRQ detected SB uses *)
DSP_Addr:Word; (* Baseaddress detected SB uses *)
DMA_Channel:Byte; (* DMA channel for 8 Bit play *)
DMA_16BitChannel:byte; (* DMA channel for 16 Bit play *)
PROCEDURE Forceto(typ,dma,dma16,irq:byte;dsp:word); (* force to use these values for playing *)
FUNCTION UseBlasterEnv:boolean; (* use values set in enviroment BLASTER *)
FUNCTION InputSoundblasterValues:Boolean; (* Input Soundbaster values by hand (in textmode) *)
FUNCTION DetectSoundblaster(prot:boolean):Boolean; (* detects Soundblastercard *)
FUNCTION Detect_DSP_Addr(prot:boolean):Boolean; (* detects DSP ADDR *)
FUNCTION Detect_DMA_Channel_IRQ(prot:boolean):Boolean; (* detects DMA Channel,DSPIRQ *)
FUNCTION Get_BlasterVersion:Word; (* reads the detected soundblaster version *)
procedure check_samplerate(var rate:word;var stereo:boolean); (* check min/max samplerate *)
procedure set_DMAvalues(p:pointer;Length:word;autoinit:boolean);
(* config DMAcontroller for different transfer modes *)
procedure play_firstBlock(length:word);
(* set the SBinterrupt to "interrupt" every "length" bytes
the best way to use it, is to play the half buffersize and then
write the new data in the allready played part
- you have to setup DMA controller by calling set_DMAvalues *)
PROCEDURE play_oneBlock(p:pointer;length:word);
(* it's a play routine for playing only one buffer (NOT CONTINOUSLY
PLAYING ! - it does not work in that way on a SB16 !)
we use non autoinit mode here for all SBs...
this proc. setup DMA controller *)
PROCEDURE Initblaster(var frequ:Word;stereoon,_16Biton:Boolean);
(* set frequency and stereo/mono and 8/16 Bit mode *)
PROCEDURE wr_mixerreg(reg,wert:byte);
(* writes something to the mixing chip *)
FUNCTION rd_mixerreg(reg:byte):byte;
(* reads something from the mixing chip *)
PROCEDURE set_ready_irq(p:pointer);
(* set user irq - (irq if buffer ends !)
attention look at my default irq !
You need those two port commands you'll find there !
And pay attention to 16bit/8bit mode on SB16 (different
ackknowledgement ports) *)
PROCEDURE stop_play; (* stops playing ! *)
PROCEDURE pause_play; (* stops playing, but you can continue
with "continue_play" *)
PROCEDURE continue_play; (* continues playing after pause play *)
PROCEDURE restore_irq; (* restore old interrupt vector *)
PROCEDURE set_sign(signed:boolean); (* sets flag to play signed data
- does only work on SB16 (set it before start playing) *)
PROCEDURE setvolume(vol:Byte); (* what do you think ? *)
PROCEDURE speaker_on; (* Does not work on SB16 *)
PROCEDURE speaker_off;
procedure write_zaehler; (* It's for 8 & 16 Bit mode to get the DMA counter *)
function get_zaehler:word; (* It's for 8 & 16 Bit mode to get the DMA counter *)
procedure writelnSBConfig; (* what do you expect ? - write current setup to screen,
but detect SB before calling that proc. *)
Implementation
uses dos,crt;
{ Flags and variables for detect part : }
VAR SB_Detect:Boolean; { Flag if SB is detected }
DSPIRQ_Detect:Boolean; { Flag if IRQ number is detected }
DSPADR_Detect:Boolean; { Flag if Baseaddress is detected }
DMACHN_Detect:Boolean; { Flag if DMAchannel is detected }
MIXER_Detect:Boolean; { Flag if Mixerchip is detected }
SBVersHi:Byte; { Soundblaster version major }
SBVersLo:Byte; { Soundblaster version minor }
check:byte; { for detecting }
savvect:pointer; { " " }
{ Soundblaster handling : }
function try_reset(p:word):Boolean; assembler;
asm
mov bl,1
mov dx,p
add dx,6
mov al,1 { write 1 to port 2x6 }
out dx,al
in al,dx
in al,dx
in al,dx
in al,dx
xor al,al
out dx,al { after 3,3 µs write 0 to port 2x6 }
{ And now check the answer }
add dx,8
mov si,200
@@readloop:
mov cx,0ffffh { SB2.0/1.0 are that slow :( }
@@testl: { check for data available }
in al,dx
dec cx
jz @@not
or al,al
jns @@testl
sub dx,4
in al,dx { read data comming through }
cmp al,0aah
je @@aSB
add dx,4
dec si
jnz @@readloop
@@not: mov bl,0 { it's not a SB :( }
@@aSB: xor ah,ah
mov al,bl
end;
procedure wr_dsp; assembler;
{ it's WAITWRITE and then WRITE IT }
asm
push bx
push cx
mov bh,al
mov dx,dsp_addr
add dx,0ch
mov cx,0ffffh { ya know, slow SBs }
{ Wait for writing : }
@@litl: in al,dx
dec cx
jz @@ende
or al,al
js @@litl { check bit 7 if we can write to port 2xC }
mov al,bh
out dx,al { write it }
@@ende: pop cx
pop bx
end;
procedure speaker_off;
begin
asm
mov al,0d3h
call wr_dsp
push 220 { needs a bit time to switch it off }
call far ptr delay
end;
end;
procedure speaker_on;
begin
asm
mov al,0d1h
call wr_dsp
push 110 { needs a bit time to switch it on }
call far ptr delay
end;
end;
function rd_dsp:byte; assembler;
{ It's WAITREAD and then READ byte }
asm
mov dx,dsp_addr
add dx,0eh
mov cx,0ffffh { ya know - slow SBs. You can believe me ! }
{ check for data available : }
@@litl: in al,dx
dec cx
jz @@ende
or al,al
jns @@litl { bit 7 set ? if not then wait }
sub dx,0eh-0ah
in al,dx { write data }
xor ah,ah
@@ende:
end;
procedure wr_mixerreg(reg,wert:byte); assembler;
{ this routine may not work for all registers because of different timings.}
asm
cmp [SBNo],1
je @@nomixer { SB 1.0/1.5 has no mixer ! }
cmp SBNo,3
je @@nomixer { SB 2.0/2.5 has no mixer ! }
mov al,reg
mov dx,dsp_addr
add dx,4
out dx,al
inc dx
in al,dx
mov al,wert
out dx,al
@@nomixer:
end;
function rd_mixerreg(reg:byte):byte; assembler;
asm
cmp [SBNo],1
je @@nomixer { SB 1.0/1.5 has no mixer ! }
cmp SBNo,3
je @@nomixer { SB 2.0/2.5 has no mixer ! }
mov dx,dsp_addr
add dx,4
mov al,reg
out dx,al
inc dx
in al,dx
xor ah,ah
@@nomixer:
end;
{ Sorry no cool macro like in C is possible }
function loword(l:longint):word; assembler;
asm
mov ax,word ptr(l)
end;
function hiword(l:longint):word; assembler;
asm
mov ax,word ptr(l+2)
end;
procedure set_DMAvalues(p:pointer;Length:word;autoinit:boolean);
{ If you want to know more about how to setup DMA controller, please
refer to our documentation SBLASTER.ZIP. }
const pagetable:array[0..7] of word = ($0087 { channel 0 },
$0083 { channel 1 },
$0081 { channel 2 <- not used by SB },
$0082 { channel 3 },
$008F { channel 4 <- not used by SB },
$008B { channel 5 },
$0089 { channel 6 },
$008A { channel 7 });
begin
asm
{ start : }
cmp [_16Bit],1 { setup 16bit DMA channels 4..7 is
ab bit different }
je @@higherDMA
{ first the SBPRO stereo bugfix : }
cmp [stereo],0
je @@nostereo
cmp [sbNo],6
jae @@sbhigher
{ well ... should be a SB PRO in stereo mode ... }
{ let's send one byte ! }
mov al,10h
call wr_dsp
mov al,128 { nothin but silence ! }
call wr_dsp
@@sbhigher:
@@nostereo:
{ convert pointer in realadress :
dmapage*65536+dmaoffset = memsegment*16 + memoffset }
mov ax,word ptr(p+2)
rol ax,4
mov cl,al
and al,0f0h
and cl,0fh
mov di,ax { cl:di - realadress ! }
mov bh,dma_channel { bh with dma_channel }
mov bl,bh
shl bl,1 { bl with dma_channel*2 }
mov ch,048h { ch = 010010xx }
{ \| |+- read
| +- autoinit flag
+- singlemode }
mov al,[autoinit]
shl al,4
or ch,al { set the autoinit flag }
add ch,dma_channel { prepare for dma_channel }
mov al,4
add al,bh { bh = dma_channel }
out 0ah,al { mask the channel }
xor al,al
out 0ch,al { clear flipflop }
mov al,ch { ch = DMAmode }
out 0bh,al { set dmatransfer mode }
mov ax,di { di = DMAbuffer offset }
push bx
xor bh,bh
mov dx,bx { bx = 2*dma_channel }
out dx,al { lower adress }
mov al,ah
out dx,al { higer adress }
mov dx,word ptr (pagetable+bx)
mov al,cl { cl = DMAbuffer page }
out dx,al { data page }
mov dx,bx { bx = 2*dma_channel }
mov ax,Length
dec ax
inc dx { dx = write base count }
out dx,al { write lower length }
mov al,ah
out dx,al { write higer length }
pop bx
mov al,dma_channel
out 0ah,al { demask channel }
jmp @@endofsetauto
@@higherDMA: { jump to here if 16bit DMA setup }
{ convert pointer in realadress : }
mov ax,word ptr(p+2) { ax = segment of buffer }
rol ax,4 { attention no offset ! }
mov cl,al
and al,0f1h
and cl,00eh
ror ax,1
mov di,ax { cl:di - realadress ! }
mov bh,dma_16Bitchannel { bh with dma_channel }
sub bh,4 { channel 4-7 to number 0-3 }
mov bl,bh
shl bl,2
add bl,0c0h { bl = DMA adressport for current DMAchannel }
{ bh = DMA16Bitchannel - 4 (0..3) }
mov ch,048h { ch = 010010xx }
{ \| |+-read
| +-autoinitflag
+-singlemode }
mov al,[autoinit]
shl al,4
or ch,al { set the autoinitflag }
add ch,bh { ch = command for DMAchannel # }
mov al,4
add al,bh { bh = 16bitDMA_channel - 4 }
out 0d4h,al { mask the channel }
xor al,al
out 0d8h,al { clear flipflop }
mov al,ch { ch = dmamode }
out 0d6h,al { set dmatransfer mode }
mov ax,di { lower part of adress }
push bx
xor bh,bh { bx now = c0h/c4h/c8h/cch (0..3) }
mov dx,bx { bx = addressport for current channel }
out dx,al { write lower adress }
mov al,ah
out dx,al { write higer adress }
mov dl,dma_16Bitchannel
xor dh,dh
mov si,dx { si = dma16Bitchannel }
shl si,1 { si - position in pagetable }
mov dx,word ptr (pagetable+si)
mov al,cl { dmabuffer page }
out dx,al { data page }
mov dx,bx { old dx value ;) c0h/c4h/c8h/cch }
mov ax,Length
dec ax
add dx,2 { seperated by 2 -> dx now DMA base count }
out dx,al { write lower length }
mov al,ah
out dx,al { write higer length }
pop bx
mov al,bh
out 0d4h,al { demask channel }
@@endofsetauto:
end;
end;
function get_zaehler:word; assembler;
{ get the dma base counter of dmachannel is used by SB
you can check if sound transfer does work ;) }
asm
cmp [_16Bit],1
je @@get16
xor al,al
out 0ch,al { clear flipflop }
mov dl,dma_channel
xor dh,dh
shl dx,1
inc dx { dx = channel * 2 + 1 = base counter }
in al,dx { al = lower byte }
mov bl,al
in al,dx { al = higher byte }
mov bh,al
mov ax,bx { AX = high and low part together ;) - return that }
{ bytes left to send = ax + 1 }
jmp @@endofget
@@get16:
xor al,al
out 0d8h,al { clear flipflop }
mov dl,dma_16Bitchannel
xor dh,dh
sub dl,4 { channel 4..7 to number 0..3 }
shl dx,2
add dx,0c2h { dx = 0c2h + 4 * (channel-4) = 16bit base counter }
in al,dx { AL = lower part }
mov bl,al
in al,dx { AL = higher part }
mov bh,al
mov ax,bx { AX = 16bit value ;) -
number WORDS (!) left to send = ax + 1 }
@@endofget:
end;
procedure write_zaehler;
{ A stupid function I know, but get_zaehler did not exist in testphase
of my player, that was all in write_zaehler implemented, but later I
thought it would be usefull to implement get_zaehler for debugging. }
begin
write(' ',get_zaehler,' ');
end;
procedure play_firstBlock(length:word);
{ call this if you want to do continues play }
begin
asm
cmp [SBNo],6
je @@sb16init { use special commands on SB16 }
mov bl,90h { DSP 90h - autoinit highspeed DMA }
cmp [SBNo],1
jne @@highspeed { >SB1.0 use highspeed modes }
{ for SB1.0 : }
mov bl,1ch { DSP 1Ch - autoinit normal DMA }
@@highspeed:
mov cx,length
dec cx
mov al,048h { DSP 48h - setup DMA buffer size }
call wr_dsp
mov al,cl { lower part of size }
call wr_dsp
mov al,ch { higher part of size }
call wr_dsp
mov al,bl { DSP command depends on SB }
call wr_dsp
jmp @@ende
@@sb16init: mov cx,length
dec cx
cmp [_16Bit],1 { other command for 16bit play ... }
je @@play16Bit
mov al,0c6h { DSP c6h - use 8bit autoinit }
call wr_dsp
mov al,signeddata
shl al,4 { 2nd command byte: bit 4 = 1 - signed data }
cmp [stereo],0
je @@nostereo
or al,020h { 2nd command byte: bit 5 = 1 - stereo data }
@@nostereo: call wr_dsp { write 2nd command byte }
mov al,cl { lower part of size }
call wr_dsp
mov al,ch { higher part of size }
call wr_dsp
jmp @@ende
@@play16Bit: mov al,0B6h { DSP B6h - use 16bit autoinit }
call wr_dsp
mov al,signeddata
shl al,4 { 2nd command byte: bit 4 = 1 - signed data }
cmp [stereo],0
je @@nostereo2
or al,020h { 2nd command byte: bit 5 = 1 - stereo data }
@@nostereo2: call wr_dsp { write 2nd command byte }
mov al,cl { lower part of size }
call wr_dsp
mov al,ch { higher part of size }
call wr_dsp
@@ende:
end;
end;
PROCEDURE play_oneBlock(p:pointer;length:word);
{ call this if you want to play only ONE (!) block - I'm sure you can do
continues play with this proc. on SBs <SB16 (I have seen that often in
other sources, but it'll definitly not work on a SB16 ! It'll cause
'ticks' }
begin
set_DMAvalues(p,length,false);
asm
cmp [SBNo],6
je @@sb16init { use special commands on SB16 }
mov bl,91h { DSP 91h - nonautoinit highspeed DMA }
cmp [SBNo],1
je @@highspeed { >SB1.0 use highspeed mode }
{ On SB1.0 : }
mov bl,14h { DSP 14h - nonautoinit normal DMA }
@@highspeed:
mov cx,length
dec cx
mov al,048h { DSP 48h - setup DMA buffer size }
call wr_dsp
mov al,cl { lower part of size }
call wr_dsp
mov al,ch { higher part of size }
call wr_dsp
mov al,bl { DSP command depends on SB }
call wr_dsp
jmp @@ende
@@sb16init: mov cx,length
dec cx
cmp [_16Bit],1 { other command for 16bit play ... }
je @@play16Bit
mov al,0c2h { DSP c2h - use 8bit nonautoinit }
call wr_dsp
mov al,signeddata
shl al,4 { 2nd command byte: bit 4 = 1 - signed data }
cmp [stereo],0
je @@nostereo
or al,020h { 2nd command byte: bit 5 = 1 - stereo data }
@@nostereo: call wr_dsp { write 2nd command byte }
mov al,cl { lower part of size }
call wr_dsp
mov al,ch { higher part of size }
call wr_dsp
jmp @@ende
@@play16Bit: mov al,0B2h { DSP B2h - use 16bit nonautoinit }
call wr_dsp
mov al,signeddata
shl al,4 { 2nd command byte: bit 4 = 1 - signed data }
cmp [stereo],0
je @@nostereo2
or al,020h { 2nd command byte: bit 5 = 1 - stereo data }
@@nostereo2: call wr_dsp { write 2nd command byte }
mov al,cl { lower part of size }
call wr_dsp
mov al,ch { higher part of size }
call wr_dsp
@@ende:
end;
end;
{ -------------------- continue commenting here ---------------------- }
PROCEDURE SetTimeConst(tc:byte);
{ Setup samplerate with time constant, take this :
TC = 256- TRUNC(1000000/SAMPLERATE) }
begin
asm
mov al,040h
call wr_dsp
mov al,tc
call wr_dsp
end;
end;
PROCEDURE Initblaster(var frequ:Word;stereoon,_16Biton:boolean);
{ Initblaster does this : 1. check samplerates for its borders
2. Reset DSP chip
3. setup samplerate
4. setup stereo/mono mode
if you want to play signed data on SB16, call 'set_sign' after Initblaster }
var tc:byte;
w:word;
begin
{ first reset SB : }
asm
mov dx,dsp_addr
add dx,0eh
in al,dx
inc dx
in al,dx
end;
stop_play;
{ Now init : }
check_samplerate(frequ,stereoon);
_16bit:=(SBNo=6) and _16Biton;
stereo:=stereoon;
{ calculate timeconstant - pay attention on SB PRO you have to setup
2*samplerate in stereo mode (so call it byterate) - on SB16 not ! }
if (sbno=6) or not stereo then
begin
tc:=256-1000000 div frequ;
frequ:=1000000 div (256-tc);
end
else
begin
tc:=256-1000000 div (2*frequ);
frequ:=(1000000 div (256-tc)) div 2;
end;
w:=frequ;
try_reset(dsp_addr);
{ set sampling rate }
if (sbno<6) then
asm
{ on all normal SB's :) }
mov al,040h
call wr_dsp
mov al,tc
call wr_dsp
end
else
asm
{ on SB16 }
mov al,041h
call wr_dsp
mov ax,w
xchg al,ah
call wr_dsp
mov al,ah
call wr_dsp
end;
{ setup stereo option on SB PRO - on SB16 it's set in DSP command }
if stereo and (SBNo<>6) then
wr_mixerreg($0e,rd_mixerreg($0e) or $02); { stereo option on (only SB PRO) }
if SBNo in [2,4,5] then
wr_mixerreg($0e,rd_mixerreg($0e) or $20); { filter option off (only SB PRO) }
speaker_on;
end;
{ -------------- now the procedures for my old autodetection ------------- }
{ No comments about it - it's old ;) }
procedure irq2;interrupt;var a:byte; begin check:=2;port[$20]:=$20;a:=port[dsp_addr+$0e] end;
procedure irq5;interrupt;var a:byte; begin check:=5;port[$20]:=$20;a:=port[dsp_addr+$0e] end;
procedure irq7;interrupt;var a:byte; begin check:=7;port[$20]:=$20;a:=port[dsp_addr+$0e] end;
procedure ready_irq; interrupt;var a:byte; begin check:=1;port[$20]:=$20;a:=port[dsp_addr+$0e] end;
function hexword(w:word):string;
const hex:string= '0123456789ABCDEF';
begin
hexword:=hex[hi(w) div 16+1]+hex[hi(w) mod 16+1]+hex[lo(w) div 16+1]+hex[lo(w) mod 16+1];
end;
FUNCTION Detect_DSP_Addr(prot:boolean):Boolean;
var p:word;
begin
if dspadr_detect then begin detect_dsp_addr:=true; exit end;
if prot then writeln(' Now locating DSP-Adresse :'#13#10);
detect_dsp_addr:=false;
p:=$210;
while not dspadr_detect and (p<$290) do
begin
if prot then write(' Trying ',hexword(p),' .... ');
dspadr_detect:=try_reset(p);
if not dspadr_detect then
begin
inc(p,$10);
if prot then write('not ');
end;
if prot then writeln('succesfull ');
end;
if not dspadr_detect then exit;
dsp_addr:=p;
detect_dsp_addr:=true;
end;
PROCEDURE reset_mixer;
begin
asm
mov dx,dsp_addr
add dx,4
mov al,0
out dx,al
mov cx,50
@@loop: loop @@loop
inc dx
inc al
out dx,al
end;
end;
FUNCTION Detect_DMA_Channel_IRQ(prot:boolean):Boolean;
const irqs:array[1..3] of byte = (10,13,15); (* IRQ 2,5,7 *)
var oldv:array[1..5] of pointer;
i,nr:byte;
fr:word;
ov1,ov2:byte;
begin
asm
mov al,0ffh
out 0fh,al
sti
end;
if dmachn_detect then begin detect_DMA_Channel_irq:=true;exit end;
if prot then writeln(#13#10' Now locating DMA-channel and IRQ :'#13#10);
detect_dma_channel_irq:=false;
if not dspadr_detect then exit;
for i:=1 to 3 do
begin
getintvec(irqs[i],oldv[i]);
end;
setintvec(10,addr(irq2));
setintvec(13,addr(irq5));
setintvec(15,addr(irq7));
Detect_DMA_Channel_irq:=false;
port[$21]:=port[$21] and $5F; { 01011111b = 05Fh }
nr:=0;
while (nr<4) and not DMACHN_Detect do
begin
if prot then write(' Trying Channel ',nr,' .... ');
Check:=0;
DMA_Channel:=nr;
fr:=10000;
asm
mov al,dma_channel { mask channel - means stop transfer }
out 0ah,al
end;
stop_play;speaker_off;
Initblaster(fr,false,false);
play_oneblock(ptr(0,0),1);
delay(10);
DMACHN_Detect:=check<>0;
if not DMACHN_Detect then
begin
inc(nr);if nr=2 then nr:=3;
if prot then write('not ');
end;
if prot then
begin
write('sucessful');
if DMACHN_Detect then writeln(' with Interrupt ',IRQ_Table[check],' - IRQ ',check)
else writeln;
end;
end;
port[$21]:=port[$21] or $A0; { 10100000b = 0A0h }
for i:=1 to 3 do
setintvec(irqs[i],oldv[i]);
if not dmachn_detect then exit;
Detect_DMA_Channel_irq:=true;
DSPIRQ_detect:=true;
IRQ_no:=Check;
try_reset(dsp_addr);
end;
procedure Fix_blastertype;
var b1,b2:byte;
begin
asm
mov al,0E1h { DSP E1h - get DSP version }
call wr_dsp
end;
sbversHi:=rd_dsp;
sbversLo:=rd_dsp;
end;
FUNCTION DetectSoundblaster(prot:boolean):Boolean;
begin
SBNo:=0;
DetectSoundblaster:=false;
SB_Detect:=False;
DSPIRQ_Detect:=false;
DSPADR_Detect:=false;
DMACHN_Detect:=False;
MIXER_Detect:=False;
stereo_possible:=false;
_16Bit_possible:=false;
STEREO:=False;_16Bit:=False;
if not Detect_DSP_Addr(prot) then
begin
if prot then writeln(' Can'#39't locate DSP-addresse ! ');
exit;
end;
fix_blastertype;
if (sbversHi<1) or (sbversHi>4) then
begin
if prot then writeln(' Sorry, unknown DSP chip version on this base address detected.');
SBno:=0;
exit;
end;
{ for the first set SB1.0 - should work on all SBs }
SBNo:=1;stereo_possible:=false;_16Bit_possible:=false;
maxmonorate:=22050;maxstereorate:=0;
stop_play;
if not Detect_DMA_Channel_irq(prot) then
begin
if prot then writeln(' Can'#39't locate DMA-channel and IRQ ! ');
sbNo:=0;
exit;
end;
try_reset(dsp_addr);
{ SBvers:
SoundBlaster 1.0/1.5 1.xx
SoundBlaster 2.0/2.5 2.xx
SoundBlaster Pro/PRO3/PRO4 3.xx
SoundBlaster 16/ASP 4.xx
}
case sbversHi of
1: begin
SBNo:=1;stereo_possible:=false;_16Bit_possible:=false;
maxmonorate:=22050;maxstereorate:=0
end;
2: begin
SBNo:=3;stereo_possible:=false;_16Bit_possible:=false;
maxmonorate:=44100;maxstereorate:=0
end;
3: begin
SBNo:=2;stereo_possible:=true;_16Bit_possible:=false;
maxmonorate:=44100;maxstereorate:=22700
end;
4: begin
SBNo:=6;stereo_possible:=true;_16Bit_possible:=true;
maxmonorate:=45454;maxstereorate:=45454
end;
else begin SBNo:=0;exit end;
end;
DetectSoundblaster:=true;
end;
FUNCTION Get_BlasterVersion:Word;
begin
Get_BlasterVersion:=word(SBVersHi)*256+SBVersLo;
end;
PROCEDURE set_ready_irq(p:pointer);
var b:byte;
begin
check:=0;
getintvec(IRQ_Table[irq_no],savvect);
if p=Nil then p:=addr(ready_irq);
setintvec(IRQ_Table[irq_no],p);
b:=1 shl irq_no;b:=b or 04; { no changes for IRQ2 }
port[$21]:=port[$21] and not b; { masking ... }
end;
PROCEDURE restore_irq;
var b:byte;
begin
b:=1 shl irq_no;b:=b and not 4; { no mask for IRQ2 }
port[$21]:=port[$21] or b;
setintvec(IRQ_Table[irq_no],savvect);
end;
FUNCTION ready:boolean;
begin
ready:=check>0;
end;
PROCEDURE stop_play;
begin
{ for 16bit modes : }
asm
mov al,0d0h
call wr_dsp
mov al,0d9h
call wr_dsp
mov al,0d0h
call wr_dsp
end;
{ for 8bit modes : }
asm
mov al,0d0h
call wr_dsp
mov al,0dah
call wr_dsp
mov al,0d0h
call wr_dsp
end;
try_reset(dsp_addr); { reset is the best way to make sure SB stops playing ! }
asm
mov al,dma_channel
out 0ah,al
end;
end;
PROCEDURE pause_play;
begin
if not _16bit then
asm
mov al,0D0h
call wr_dsp
end
else
asm
mov al,0D5h
call wr_dsp
end
end;
PROCEDURE continue_play;
begin
if not _16bit then
asm
mov al,0D4h
call wr_dsp
end
else
asm
mov al,0D6h
call wr_dsp
end
end;
PROCEDURE set_sign(signed:boolean);
begin
signeddata:=signed;
end;
procedure setfilter(how:boolean);
var b:byte;
begin
b:=rd_mixerreg($0e);
if how then { on } b:=b or $20 else b:=b and not $20;
wr_mixerreg($0e,b); { switch the filter option }
end;
procedure setvolume(vol:byte);
var b:byte;
begin
if sbno<6 then
begin
if vol>=15 then vol:=15;
b:=vol;
b:=b shl 4; { the other side }
vol:=b+vol;
wr_mixerreg($22,vol);
wr_mixerreg($04,vol);
end
else
begin
{ on SB16 the new mixer registers :) }
wr_mixerreg($30,vol); { master left }
wr_mixerreg($31,vol); { master right }
wr_mixerreg($32,vol); { Voice left }
wr_mixerreg($33,vol); { Voice right }
end;
end;
PROCEDURE Forceto(typ,dma,dma16,irq:byte;dsp:word);
begin
SB_Detect:=true;
DSPIRQ_Detect:=true;
DSPADR_Detect:=true;
DMACHN_Detect:=true;
stereo:=false;
_16Bit:=false;
MIXER_detect:=typ>1;
stereo_possible:=typ in [2,4,5,6];
_16Bit_possible:= typ=6;
IRQ_No:=irq;
DSP_Addr:=dsp;
DMA_Channel:=DMA;
DMA_16BitChannel:=dma16;
SBNo:=typ;
case typ of
1: begin maxmonorate:=22050;maxstereorate:=0 end;
2: begin maxmonorate:=44100;maxstereorate:=22050 end;
3: begin maxmonorate:=44100;maxstereorate:=0 end;
4: begin maxmonorate:=44100;maxstereorate:=22050 end;
5: begin maxmonorate:=44100;maxstereorate:=22050 end;
6: begin maxmonorate:=45454;maxstereorate:=45454 end;
end;
end;
function UseBlasterEnv:boolean;
var s:string;
typ,dma,dma16,irq:byte;
dsp:word;
function upstr(s:string):string;
var t:string;
i:byte;
begin
t:='';
for i:=1 to length(s) do
t:=t+upcase(s[i]);
upstr:=t;
end;
var count,i:byte;
u:string;
er:integer;
begin
typ:=255;dma:=255;dma16:=255;irq:=255;dsp:=$ffff;
{ default values (totally crap), but if you get them after calling Use....}
{ you'll know that this value is not/wrong defined in the BLASTER env. }
UseBlasterEnv:=false;
s:=upstr(getenv('BLASTER'));
if s='' then exit; { no chance :( }
count:=0;
{ SET BLASTER=A220 I? D? H? P??? T? }
i:=pos('T',s); { Soundblaster typ }
if i>0 then
begin
u:=copy(s,i+1,1); { maybe for future blaster versions not right :( }
val(u,typ,er);
if er=0 then inc(count); { yeah we got this value ! }
end;
i:=pos('D',s); { DMAchannel }
if i>0 then
begin
u:=copy(s,i+1,1);
val(u,dma,er);
if (er=0) and (dma<4) and (dma<>2) then inc(count) { yeah we got it ! }
else dma:=255;
end;
i:=pos('I',s); { IRQ number }
if i>0 then
begin
if s[i+2]<>' ' then u:=copy(s,i+1,2) else u:=copy(s,i+1,1);
val(u,irq,er);
if (er=0) and ((irq=2) or (irq=5) or (irq=7) or (irq=10)) then inc(count)
else irq:=255;
end;
i:=pos('H',s); { 16Bit DMAchannel }
if i>0 then
begin
u:=copy(s,i+1,1);
val(u,dma16,er);
if (er<>0) or (dma16<5) or (dma16>9) then dma16:=255;
{ it does not matter if there's no value }
end;
i:=pos('A',s); { DSPadress }
if i>0 then
begin
u:=copy(s,i+1,3);
val(u,dsp,er);
dsp:=(dsp div 100)*256+ ((dsp div 10) mod 10)*16 + dsp mod 10;
if (er=0) and (dsp div 256 = 2) and (((dsp mod 256) div 16) in [2,3,4,5,6,7,8]) then inc(count)
else dsp:=$ffff;
end;
if count=4 then { we got all :) }
forceto(typ,dma,dma16,irq,dsp)
else exit; { was not enough detectable }
UseBlasterEnv:=true;
end;
procedure writelnSBConfig;
begin
writeln(#13#10' Soundblaster typ: ');
case sbNo of
0: writeln(' none ');
1: writeln(' Soundblaster 1.0/1.5 (8 bit/mono-max 24kHz)');
2: writeln(' Soundblaster Pro (8 bit/mono-max 44kHz/stereo-max 22kHz)');
3: writeln(' Soundblaster 2.0/2.5/junior (8 bit/mono-max 44kHz)');
4: writeln(' Soundblaster Pro3.0/PRO 4.0 (8 bit/mono-max 44kHz/stereo-max 22kHz)');
5: writeln(' Soundblaster Pro<microchannel> (8 bit/mono-max 44kHz/stereo-max 22kHz)');
6: writeln(' Soundblaster 16/16 ASP (8/16 bit/mono/stereo/max 45kHz)');
end;
write(#13#10' SB-Base : 2');write((dsp_addr div 16) mod 16);writeln('0h');
writeln(' 8bit DMA : ',dma_channel);
if SBNo=6 then writeln(' 16bit DMA : ',dma_16Bitchannel);
writeln(' IRQ : ',IRQ_No,#13#10);
end;
FUNCTION InputSoundblasterValues:Boolean;
var c:char;
begin
InputSoundblasterValues:=false;
writeln(#13#10' Soundblaster typ ? ');
writeln(' 0) none ');
writeln(' 1) Soundblaster 1.0/1.5 ..... 8 bit mono (max 24kHz)');
writeln(' 2) Soundblaster 2.0/2.5/junior ..... 8 bit mono (max 44kHz)');
writeln(' 3) Soundblaster Pro/Pro3.0/PRO 4.0/micro ..... 8 bit stereo (max 22kHz)');
writeln(' 4) Soundblaster 16/16 ASP ..... 16 bit stereo (max 45kHz)');
repeat c:=readkey; until (c in ['0','1','2','3','4']);
case c of
'0': exit;
'1': SBNo:=1;
'2': SBNo:=3;
'3': SBNo:=4; { also 2,5 }
'4': SBNo:=6;
end;
writeln(#13#10' Soundblaster baseport 2X0h ?');
write(' X = ');
repeat c:=readkey; until (c in ['0'..'9']);
writeln(c);
dsp_addr:=$200+$10*(ord(c)-ord('0'));
write(#13#10' 8 bit DMA channel (0,1,3) ? ');
repeat c:=readkey; until (c in ['0','1','3']);
writeln(c);
dma_channel:=ord(c)-ord('0');
if SBNo=6 then
begin
write(#13#10' 16 bit DMA (5,6,7) ? ');
repeat c:=readkey; until (c in ['5','6','7']);
writeln(c);
DMA_16bitchannel:=ord(c)-ord('0');
end;
write(#13#10' IRQ (2,5,7) ? ');
repeat c:=readkey; until (c in ['2','5','7']);
writeln(c);
IRQ_No:=ord(c)-ord('0');
forceto(SBNo,dma_channel,dma_16bitchannel,IRQ_No,dsp_addr);
InputSoundblasterValues:=true;
end;
procedure check_samplerate(var rate:word;var stereo:boolean);
begin
stereo:=stereo and stereo_possible;
if rate<4000 then rate:=4000;
if stereo then
begin
if rate>maxstereorate then rate:=maxstereorate;
end
else
if rate>maxmonorate then rate:=maxmonorate;
end;
begin
SB_Detect:=False;
DSPIRQ_Detect:=false;
DSPADR_Detect:=false;
DMACHN_Detect:=False;
MIXER_Detect:=False;
stereo_possible:=false;
_16Bit_possible:=false;
STEREO:=False;_16Bit:=False;signeddata:=false;
SBVersHi:=0;SBVersLo:=0;
SBno:=0;
IRQ_No:=7;
DSP_Addr:=$220;
DMA_Channel:=1;
DMA_16Bitchannel:=5;
end.